package cn.ztr.ymcc.service.impl;

import cn.ztr.ymcc.constance.cons;
import cn.ztr.ymcc.domain.CourseOrder;
import cn.ztr.ymcc.domain.CourseOrderItem;
import cn.ztr.ymcc.dto.KillOrderDto;
import cn.ztr.ymcc.dto.OrderDto;
import cn.ztr.ymcc.dto.PayDto;
import cn.ztr.ymcc.mapper.CourseOrderItemMapper;
import cn.ztr.ymcc.mapper.CourseOrderMapper;
import cn.ztr.ymcc.result.JsonResult;
import cn.ztr.ymcc.service.ICourseOrderService;
import cn.ztr.ymcc.util.AssertUtil;
import cn.ztr.ymcc.util.CodeGenerateUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ztr
 * @since 2023-03-08
 */
@Service
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {

    @Autowired
    private CourseOrderItemMapper courseOrderItemMapper;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 普通订单下单
     * @param orderDto
     * @return
     */
    @Override
    @Transactional
    public JsonResult placeOrder(OrderDto orderDto,Long loginId) {
        List<CourseOrderItem> courseOrderItems = orderDto.getCourseOrderItems();
        //1 添加订单信息
        //1.1 生成订单编号
        String orderNo = CodeGenerateUtils.generateOrderSn(32);

        //1.2 封装参数 添加订单编号 支付类型 订单中的商品数量 用户id
        CourseOrder courseOrder = new CourseOrder()
                .setOrderNo(orderNo)
                .setPayType(orderDto.getPayType())
                .setTotalCount(courseOrderItems.size())
                .setUserId(loginId);

        //1.3 添加订单优惠金额,总金额,支付金额
        Double totlePrice = 0d;
        Double PaymentAmount=0d;
        for (CourseOrderItem courseOrderItem : courseOrderItems) {
            //总金额
            totlePrice += courseOrderItem.getPrice().doubleValue();
            //支付金额
            PaymentAmount += courseOrderItem.getAmount().doubleValue();
        }
        courseOrder
                .setTotalAmount(BigDecimal.valueOf(totlePrice))
                .setPayAmount(BigDecimal.valueOf(PaymentAmount))
                .setDiscountAmount(BigDecimal.valueOf(totlePrice-PaymentAmount));

        //1.4 添加明细到订单信息中
        courseOrder.setCourseOrderItems(orderDto.getCourseOrderItems());

        //2 封装支付信息
        //2.1获取条目id
        List<String> courseIds = courseOrderItems.stream().map(course->course.getCourseId().toString()).collect(Collectors.toList());
        //2.2 封装paydto
        PayDto payDto = new PayDto()
                .setPayType(courseOrder.getPayType())
                .setOrderNo(courseOrder.getOrderNo())
                .setSubject(courseOrder.getTitle())
                .setAmount(courseOrder.getPayAmount())
                .setUserId(courseOrder.getUserId())
                .setExtParams("courseIds="+String.join(",",courseIds));


        /*
        3 发送事务消息
            参数一:生产者组,
            参数二:主题和订阅,
            参数三:发送给事务监听器的消息,
            参数四:发送给消费者的消息
         */
        rocketMQTemplate.sendMessageInTransaction(
                "txProducerGroup",
                cons.TX_TOPIC_ZTR + ":" + cons.TX_TARGET_ZTR,
                MessageBuilder.withPayload(JSON.toJSONString(payDto)).build(),
                courseOrder
        );

        return JsonResult.me(orderNo);
    }

    /**
     * 秒杀下单
     * @param orderDto
     * @return
     * @throws Exception
     */
    @Override
    public JsonResult killPlaceOrder(OrderDto orderDto, Long loginId) {
        //1 合法性校验
        String orderNo = orderDto.getOrderNo();
        AssertUtil.isTrue(orderNo!=null,"没有购买资格");
        Object o = redisTemplate.opsForValue().get(orderNo);
        KillOrderDto killOrderDto = JSON.parseObject(o.toString(), KillOrderDto.class);
        AssertUtil.isTrue(killOrderDto!=null,"订单编号错误");

        //2 封装订单信息
        CourseOrder courseOrder = new CourseOrder();
        BeanUtils.copyProperties(killOrderDto,courseOrder);

        //3 封装订单条目信息
        CourseOrderItem courseOrderItem = new CourseOrderItem();
        BeanUtils.copyProperties(killOrderDto,courseOrderItem);
        courseOrder.setCourseOrderItems(Arrays.asList(courseOrderItem));

        //4 封装支付信息
        PayDto payDto = new PayDto()
                .setPayType(courseOrder.getPayType())
                .setOrderNo(courseOrder.getOrderNo())
                .setSubject(courseOrder.getTitle())
                .setAmount(courseOrder.getPayAmount())
                .setUserId(courseOrder.getUserId())
                .setExtParams("courseIds="+courseOrder.getId());

        /*
        5 发送事务消息
            参数一:生产者组,
            参数二:主题和订阅,
            参数三:发送给事务监听器的消息,
            参数四:发送给消费者的消息
         */
        rocketMQTemplate.sendMessageInTransaction(
                "txProducerGroup",
                cons.TX_TOPIC_ZTR + ":" + cons.TX_TARGET_ZTR,
                MessageBuilder.withPayload(JSON.toJSONString(payDto)).build(),
                courseOrder
        );
        return JsonResult.me(orderNo);
    }

    /**
     * 保存订单信息和订单条目信息
     * @param entity 实体对象
     * @return
     */
    @Transactional
    @Override
    public boolean save(CourseOrder entity) {
        log.debug("进入sava");
        super.save(entity);
        for (CourseOrderItem courseOrderItem : entity.getCourseOrderItems()) {
            courseOrderItem
                    .setOrderId(entity.getId())
                    .setOrderNo(entity.getOrderNo());
            courseOrderItemMapper.insert(courseOrderItem);
        }
        log.debug("正在添加");
        return true;
    }
}
